#include "disp_driver.h"
#include <stdio.h>

extern configuration config;

lcd_init_cmd_t gc9a01_init_cmds[] = {
    {0xEF, {0}, 0},
    {0xEB, {0x14}, 1},
    {0xFE, {0}, 0},
    {0xEF, {0}, 0},
    {0xEB, {0x14}, 1},
    {0x84, {0x40}, 1},
    {0x85, {0xFF}, 1},
    {0x86, {0xFF}, 1},
    {0x87, {0xFF}, 1},
    {0x88, {0x0A}, 1},
    {0x89, {0x21}, 1},
    {0x8A, {0x00}, 1},
    {0x8B, {0x80}, 1},
    {0x8C, {0x01}, 1},
    {0x8D, {0x01}, 1},
    {0x8E, {0xFF}, 1},
    {0x8F, {0xFF}, 1},
    {0xB6, {0x00, 0x00}, 2},
    {0x36, {0x40 | 0x08}, 1},
    {0x3A, {0x05}, 1},
    {0x90, {0x08, 0x08, 0x08, 0x08}, 4},
    {0xBD, {0x06}, 1},
    {0xBC, {0x00}, 1},
    {0xFF, {0x60, 0x01, 0x04}, 3},
    {0xC3, {0x13}, 1},
    {0xC4, {0x13}, 1},
    {0xC9, {0x22}, 1},
    {0xBE, {0x11}, 1},
    {0xE1, {0x10, 0x0E}, 2},
    {0xDF, {0x21, 0x0C, 0x02}, 3},
    {0xF0, {0x45, 0x09, 0x08, 0x08, 0x26, 0x2A}, 6},
    {0xF1, {0x43, 0x70, 0x72, 0x36, 0x37, 0x6F}, 6},
    {0xF2, {0x45, 0x09, 0x08, 0x08, 0x26, 0x2A}, 6},
    {0xF3, {0x43, 0x70, 0x72, 0x36, 0x37, 0x6F}, 6},
    {0xED, {0x1B, 0x0B}, 2},
    {0xAE, {0x77}, 1},
    {0xCD, {0x63}, 1},
    //{0x70, {0x07, 0x07, 0x04, 0x0E, 0x0F, 0x09, 0x07, 0x08, 0x03}, 9},
    {0xE8, {0x34}, 1},
    {0x62,
     {0x18, 0x0D, 0x71, 0xED, 0x70, 0x70, 0x18, 0x0F, 0x71, 0xEF, 0x70, 0x70},
     12},
    {0x63,
     {0x18, 0x11, 0x71, 0xF1, 0x70, 0x70, 0x18, 0x13, 0x71, 0xF3, 0x70, 0x70},
     12},
    {0x64, {0x28, 0x29, 0xF1, 0x01, 0xF1, 0x00, 0x07}, 7},
    {0x66, {0x3C, 0x00, 0xCD, 0x67, 0x45, 0x45, 0x10, 0x00, 0x00, 0x00}, 10},
    {0x67, {0x00, 0x3C, 0x00, 0x00, 0x00, 0x01, 0x54, 0x10, 0x32, 0x98}, 10},
    {0x74, {0x10, 0x85, 0x80, 0x00, 0x00, 0x4E, 0x00}, 7},
    {0x98, {0x3E, 0x07}, 2},
    {0x35, {0}, 0},
    {0x21, {0}, 0},
    {0x11, {0}, 0x80, 120},
    {0x29, {0}, 0x80, 20},
    {0, {0}, 0xff}
};

extern disp_driver_api api;
void gc9a01_api_init(void) {
    api.disp_init_cmd = gc9a01_init_cmds;
    api.disp_set_window = gc9a01_set_window;
    api.disp_set_orientation = gc9a01_set_orientation;
}
/* The ST7789 display controller can drive 320*240 displays, when using a
 * 240*240 display there's a gap of 80px, we need to edit the coordinates to
 * take into account that gap, this is not necessary in all orientations. */
void gc9a01_set_window(lv_disp_drv_t *drv, const lv_area_t *area) {
    uint8_t data[4] = {0};

    uint16_t offsetx1 = area->x1;
    uint16_t offsetx2 = area->x2;
    uint16_t offsety1 = area->y1;
    uint16_t offsety2 = area->y2;

    offsetx1 += config.offset_x;
    offsetx2 += config.offset_x;
    offsety1 += config.offset_y;
    offsety2 += config.offset_y;

    /*Column addresses*/
    disp_send_cmd(ST7789_CASET);
    data[0] = (offsetx1 >> 8) & 0xFF;
    data[1] = offsetx1 & 0xFF;
    data[2] = (offsetx2 >> 8) & 0xFF;
    data[3] = offsetx2 & 0xFF;
    disp_send_data(data, 4);

    /*Page addresses*/
    disp_send_cmd(ST7789_RASET);
    data[0] = (offsety1 >> 8) & 0xFF;
    data[1] = offsety1 & 0xFF;
    data[2] = (offsety2 >> 8) & 0xFF;
    data[3] = offsety2 & 0xFF;
    disp_send_data(data, 4);

    /*Memory write*/
    disp_send_cmd(ST7789_RAMWR);
}

void gc9a01_set_orientation(uint8_t orientation) {
    const char *orientation_str[] = {"PORTRAIT", "PORTRAIT_INVERTED",
                                     "LANDSCAPE", "LANDSCAPE_INVERTED"};

    printf("Display orientation: %s\n", orientation_str[orientation]);

    uint8_t data[] = {0x40, 0x80, 0x20, 0x10};

    disp_send_cmd(ST7789_MADCTL);
    disp_send_data((void *)&data[orientation], 1);
}